//
// Created by meow star on 2019/9/25.
//

#include "recall_manager.h"
#include "recall_types.h"
#include <plog/Log.h>
using namespace std;
int RecallManager::get_repository_status(json& result){
    for(auto it = _mp_key_2_iindex.begin(); it != _mp_key_2_iindex.end(); ++ it){
        json iindex_result;
        InvertIndexSet<EzElem>* iindex_set = it->second;
        iindex_set->get_status(iindex_result);
        result[it->first] = iindex_result;
    }
    return 0;
}
int RecallManager::stat_keywords(unordered_map<string, int>& keywords){
    for(auto it = _mp_key_2_iindex.begin(); it != _mp_key_2_iindex.end(); ++ it){
        it->second->stat_keys(keywords);
    }
    return 0;
}
int RecallManager::recall(RecallProfile *profile, RecallStrategy strategy, RecallResult& results) {
    int count = 0;
    for (map<string, vector<RecallStrategyCell>>::iterator it = strategy.recalls.begin(); it != strategy.recalls.end(); ++it){
        const string& index_key = it->first;
        auto& st_item_list = it->second;

        auto it_iindex = _mp_key_2_iindex.find(index_key);
        if (it_iindex == _mp_key_2_iindex.end()){
            PLOG_WARNING << "no such index, key=" << index_key;
            continue;
        }
        auto it_index_key = results.results.find(index_key);
        if (it_index_key == results.results.end()){
            results.results[index_key] = vector<IIndexRecallResult>();
            it_index_key = results.results.find(index_key);
        }

        auto& vec_iindex_result = results.results[index_key];
        InvertIndexSet<EzElem>* iindex = it_iindex->second;
        for (auto it_item = st_item_list.begin(); it_item != st_item_list.end(); ++ it_item){
            vector<const ElemInfo*> recall_results;
            vector<float> weights;
            spdlog::info("=======================================");
            spdlog::info("iindex->recall, index_key={}, dkey={}", index_key, it_item->key);
            iindex->recall(it_item->count, it_item->key, it_item->limit_keys, it_item->source_limit, profile->filter(), recall_results, weights);
            for (int i = 0;i < recall_results.size(); i ++){
                auto& elem = recall_results[i];
                float weight = weights[i];
                IIndexRecallResult result;
                vector<float> item_vector;
                if (!it_item->limit_keys.empty()){

                }
                //_w2v->calc_keywords_vector(elem->keywords(), item_vector);
                result.recall_key = it_item->key;
                result.weight = weight;//MathUtils::get_cosine_similarity(profile->keyword_vector,item_vector);
                result.elem = elem;
                vec_iindex_result.push_back(result);
                count += 1;
            }
        }
    }
    return count;
}

int RecallManager::reload_repository() {
    int reload_ready_count = 0;
    //下一轮来清理垃圾数据，这样可以避免加锁，又不会出现数据读写竞争
    timeval start;
    gettimeofday(&start, NULL);
    if (_fi->check_reload_object()){
        //spdlog::info("fi,count={}", _mp_key_2_iindex.size());
        for(auto it = _mp_key_2_iindex.begin(); it != _mp_key_2_iindex.end(); ++ it){
            if (it->second->check_reload_object()){
                reload_ready_count += 1;
            }
        }
    }
    if (reload_ready_count < _mp_key_2_iindex.size()){
        return 0;
    }
    //首次启动不用等待
    if (_fi->_obj_timestamp){
        spdlog::info("repo ready, repo={}, reload_ready_count={}, sleep={}", _forward_index_path, reload_ready_count, _reload_delay);
        sleep(_reload_delay);
    }

    int pid = SystemUtils::get_pid(PROCESS_NAME);
    uint64_t  vmem = SystemUtils::get_proc_virtualmem(pid);
    uint64_t mem = SystemUtils::get_proc_mem(pid);
    spdlog::info("reload forward index, vmem={}GB, mem={}GB", vmem / 1024.0 / 1024.0, mem / 1024.0 / 1024.0);
    int ret = _fi->reload_object();
    uint64_t  vmem_plus = SystemUtils::get_proc_virtualmem(pid);
    uint64_t mem_plus = SystemUtils::get_proc_mem(pid);
    spdlog::info("reload forward index, vmem+={}GB, mem+={}GB", (vmem_plus - vmem) / 1024.0 / 1024.0, (mem_plus - mem) / 1024.0 / 1024.0);
    if (ret == 0){
        for(auto it = _mp_key_2_iindex.begin(); it != _mp_key_2_iindex.end(); ++ it) {
            spdlog::info("reload invert index {}...", it->first);
            ret = it->second->reload_object();
            if (ret) {
                spdlog::error("reload {} index failed, ret={}", it->first, ret);
                break;
            }
            vmem_plus = SystemUtils::get_proc_virtualmem(pid);
            mem_plus = SystemUtils::get_proc_mem(pid);
            spdlog::info("reload invert index {}, vmem+={}GB, mem+={}GB", it->first, (vmem_plus - vmem) / 1024.0 / 1024.0, (mem_plus - mem) / 1024.0 / 1024.0);
        }
    }
    else{
        spdlog::error( "reload forward index failed, ret={}", ret);
    }
    timeval now;
    gettimeofday(&now, NULL);
    spdlog::error("repository reloaded, cost={}s", now.tv_sec - start.tv_sec);

    //这里其实应该用锁，只是
    if (ret == 0){
        _fi->update_reload_flag(now.tv_sec);
        for(auto it = _mp_key_2_iindex.begin(); it != _mp_key_2_iindex.end(); ++ it) {
            it->second->update_reload_flag(now.tv_sec);
        }


        return 1;
    }
    else{
        return ret;
    }

}

int RecallManager::init() {
    timeval start;
    gettimeofday(&start, NULL);
    int ret = reload_repository();
    timeval now;
    gettimeofday(&now, NULL);
    spdlog::info("init done, cost={}s",  now.tv_sec - start.tv_sec);
    return ret;
}